/*
 * ccu.c
 *
 *  Created on: Apr 26, 2022
 *      Author: gewenbin
 */
#define  __SYLIXOS_KERNEL
#include <SylixOS.h>
#include <linux/compat.h>

#define CCU_REG_BASE            (0x02001000)
#define CCU_REG_SMHC0_CLK       (CCU_REG_BASE + 0x830)
#define CCU_REG_SMHC_BUS        (CCU_REG_BASE + 0x84c)
#define CCU_REG_EMAC_BUS        (CCU_REG_BASE + 0x97c)

void ccu_smhc0_bus_gate_enable (int enable)
{
    u32 val;
    val = readl(CCU_REG_SMHC_BUS);

    if (enable) {
        // de assert smhc0
        writel(val | (1 << 16), CCU_REG_SMHC_BUS);
        usleep(100);
        // smhc0 gate pass
        writel(val | (1 << 0), CCU_REG_SMHC_BUS);
    } else {
        // smhc0 gate mask
        writel(val & (~(1 << 0)), CCU_REG_SMHC_BUS);
    }
}

void ccu_emac_bus_gate_enable (int enable)
{
    u32 val;
    val = readl(CCU_REG_EMAC_BUS);

    if (enable) {
        // de assert emac
        writel(val | (1 << 16), CCU_REG_EMAC_BUS);
        usleep(100);
        // emac gate pass
        writel(val | (1 << 0), CCU_REG_EMAC_BUS);
    } else {
        // emac gate mask
        writel(val & (~(1 << 0)), CCU_REG_EMAC_BUS);
    }
}

int ccu_smhc0_clock_set (unsigned int hz)
{
    unsigned int pll, pll_hz, div, n;

    if (hz <= 24000000) {
        pll = (0 << 24); // select HOSC
        pll_hz = 24000000;
    } else {
        pll = (2 << 24); // select PLL_PERI(2X)
        pll_hz = 1200000000; // PLL_PERI(2X) default 1.2GHz
    }

    div = pll_hz / hz;
    if (pll_hz % hz)
        div++;

    n = 0;
    while (div > 16) {
        n++;
        div = (div + 1) / 2;
    }

    if (n > 3) {
        _PrintFormat("mmc0 error cannot set clock to %d\n", hz);
        return -1;
    }

    writel((1 << 31) | pll | (n << 8) | (div << 0), CCU_REG_SMHC0_CLK);

    return 0;
}
